Central processing units such as microprocessors typically employ a program counter to indicate the memory address of the next instruction to be executed in a program sequence. The instruction referenced by the value or contents of the program counter is retrieved from memory and typically transferred to an instruction register for subsequent decoding and execution. After each instruction is retrieved from memory, the program counter is typically automatically incremented so that it references the following instruction in the program sequence or instruction thread.
Some instructions, such as subroutine calls, deviate from the default sequential instruction flow and require that the program counter be modified to point to the starting address of the first instruction of the subroutine. At the end of the subroutine, the program counter contents must be restored to contain the address of the next instruction to be executed from the calling instruction thread. This is accomplished by storing or pushing the contents of the program counter onto a last-in first-out (LIFO) stack at the beginning of a subroutine, and retrieving or pulling the saved program counter value from the stack at the end of the subroutine, when a return-from-subroutine instruction is encountered. The stack also enables subroutines to be nested.
The stack, which is a memory construct, may be implemented as an independent, hard-wired, series of interconnected LIFO registers. An example of such a hardwired stack is found in the PICmicro.TM. microcontroller manufactured by Microchip Technology Inc., of Chandler, Ariz. Alternatively, the LIFO stack can be implemented in a general random access memory, in which case a stack pointer register (or dedicated general memory location) is typically needed to keep track of the top of the stack.
The conventional practice of providing a program counter and associated stack for controlling program flow results in various inefficiencies when the microprocessor is required to execute multiple processes or tasks, i.e. multi-tasking, by time slicing usage of the microprocessor amongst the various tasks. The time slicing is typically administered by a core software program, often referred to as a "kernel", which typically has to save the state of the program counter and its associated stack every time the kernel temporarily terminates the execution of a given task. Later, the kernel has to restore the saved state of the program counter and its associated stack when the time comes to continue the execution of the given task. This set of events represents inefficient overhead in the sense that many memory accesses may be required to transfer the contents of multiple copies of the program counter stacks merely to switch tasks.
For example, the above mentioned PICmicro.TM. microprocessor suffers from this problem. This microprocessor exhibits a Harvard-type architecture, meaning that the program memory and data memory are physically distinct and accessed from different buses, thereby allowing an instruction to be executed while another instruction is being fetched. As is not atypical in Harvard-type architectures, the instruction length and instruction address bus is wider than the data length and data bus. (Specifically, the PICmicro.TM. has a thirteen bit wide instruction address bus to accommodate an 8K program memory and an eight bit wide data bus.) Thus, the program counter and its associated hardwired LIFO stack require two instruction cycles to transfer each entry in the program counter and associated stack to the data memory in order to store the program counter state of a given task. This is quite wasteful of processor resources.